﻿using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Kenly.DBFramework.Reflection;
using Kenly.DBFramework;
using Kenly.DBFramework.Gateway;

namespace Kenly.DBFramework.Gateway
{
    /// <summary>
    /// The gateway to a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal abstract class TableGatewayUtilityBase<T> : ITableGatewayUtility<T> where T : TableGateway<T>, new()
    {
        private DatabaseType _databaseType;
        private string _connectionString;
        
        /// <summary>
        /// 
        /// </summary>
        protected DatabaseType DatabaseType
        {
            get
            {
                return _databaseType;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected string ConnectionString
        {
            get
            {
                return _connectionString;
            }
        }

        protected TableGatewayUtilityBase(DatabaseType databaseType, string connectionString)
        {
            _databaseType = databaseType;
            _connectionString = connectionString;
        }


        protected ITableCommandExecutor CreateCommandExecutor()
        {
            IDatabase database = DatabaseFactory.CreateInstance(_databaseType);
            database.ConnectionString = _connectionString;
            return database.CreateTableCommandExecutor();
        }

        protected ICommandReflector CreateCommandReflector()
        {
            return CommandReflectorFactory.CreateInstance(_databaseType);
        }

       
        

        #region AddNew

        public abstract int AddNew(ITableCommandExecutor tableCommandExecutor, ICommandReflector commandReflector,
            T entity, bool returnIdentity);

        /// <summary>
        /// Adds an entity to table,returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int AddNew(T entity)
        {
            Transaction transaction = null;
            return AddNew(entity, false, true, out transaction);
        }

        public int AddNew(T entity, out Transaction transaction)
        {
            return AddNew(entity, false, false, out transaction);
        }

        public int AddNew(T entity, bool returnIdentity)
        {
            Transaction transaction = null;
            return AddNew(entity, returnIdentity, true, out transaction);
        }

        public int AddNew(T entity, bool returnIdentity, out Transaction transaction)
        {
            return AddNew(entity, returnIdentity, false, out transaction);
        }

        /// <summary>
        /// Adds an entity to table,returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="returnIdentity"></param>
        /// <param name="commitTransaction"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        protected abstract int AddNew(T entity, bool returnIdentity, bool commitTransaction, out Transaction transaction);
        

        public int AddNew(IList<T> entities)
        {
            Transaction transaction = null;
            return AddNew(entities, false, true, out transaction);
        }

        public int AddNew(IList<T> entities, out Transaction transaction)
        {
            return AddNew(entities, false, false, out transaction);
        }

        public int AddNew(IList<T> entities, bool returnIdentity)
        {
            Transaction transaction = null;
            return AddNew(entities, returnIdentity, true, out transaction);
        }

        public int AddNew(IList<T> entities, bool returnIdentity, out Transaction transaction)
        {
            return AddNew(entities, returnIdentity, false, out transaction);
        }

        /// <summary>
        /// Adds an array of entities into a table,returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="returnIdentity"></param>
        /// <param name="commitTransaction"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        protected abstract int AddNew(IList<T> entities, bool returnIdentity, bool commitTransaction, out Transaction transaction);
        

        #endregion


       #region UpdateByCondition

        /// <summary>
        /// Update the entity to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public int Update(T entity, string condition)
        {
            Transaction transaction = null;
            return Update(entity, condition, true, out transaction);
        }

        /// <summary>
        /// Update the entity to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="condition"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int Update(T entity, string condition, out Transaction transaction)
        {
            return Update(entity, condition, false, out transaction);
        }

        
        private int Update(T entity, string condition, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (string.IsNullOrEmpty(condition))
            {
                return 0;
            }

            List<string> propertyNames = entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }

            //not include identity
            List<IDataParameter> paras = ParameterReflection.UpdateByIdPK(_databaseType, entity, true, propertyNames);
            if (paras == null || paras.Count <= 0)
            {
                return 0;
            }

            ICommandReflector commandReflector = this.CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = this.CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);

            string updateCommandText = commandReflector.Update(typeof(T), condition, propertyNames);

            if (!commitTransaction)
            {
                tableCommandExecutor.BeginTransaction();
            }
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(updateCommandText, false, paras);
            if (affectedRowCount > 0)
            {
                entity.ClearChangedPropertyNames();
            }
            return affectedRowCount;
        }

        #endregion //UpdateByCondition


        #region UpdateById



        /// <summary>
        /// Update the entity to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int UpdateById(T entity)
        {
            Transaction transaction = null;
            return UpdateByIdPK(entity, true, true, out transaction);
        }

        /// <summary>
        /// Update the entity to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int UpdateById(T entity, out Transaction transaction)
        {
            return UpdateByIdPK(entity, true, false, out transaction);
        }


        /// <summary>
        /// Update the entity to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int UpdateByPK(T entity)
        {
            Transaction transaction = null;
            return UpdateByIdPK(entity, false, true, out transaction);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int UpdateByPK(T entity, out Transaction transaction)
        {
            return UpdateByIdPK(entity, false, false, out transaction);
        }

        /// <summary>
        /// Update the entity to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="byId"></param>
        /// <param name="commitTransaction"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        private int UpdateByIdPK(T entity, bool byId,bool commitTransaction, out Transaction transaction)
        {
            transaction = null; 
            if (entity == null)
            {
                return 0;
            }

            List<string> propertyNames =entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }
            List<IDataParameter> paras = ParameterReflection.UpdateByIdPK(_databaseType, entity, byId, propertyNames);
            if (paras == null || paras.Count <= 0)
            {
                return 0;
            }

            ICommandReflector commandReflector = this.CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = this.CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);
           
            string sql = byId ? commandReflector.UpdateById(entity, propertyNames) : commandReflector.UpdateByPK(entity, propertyNames);
            if (!commitTransaction)
            {
                tableCommandExecutor.BeginTransaction();
            }
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(sql, false, paras);
            if (affectedRowCount > 0)
            {
                entity.ClearChangedPropertyNames();
            }
            return affectedRowCount;
        }

        /// <summary>
        /// Update an array of entities to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int UpdateById(IList<T> entities)
        {
            Transaction transaction=null;
            return UpdateByIdPK(entities, true, true, out transaction);
        }

        /// <summary>
        /// Update an array of entities to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int UpdateById(IList<T> entities, out Transaction transaction)
        {
            return UpdateByIdPK(entities, true, false, out transaction);
        }

        /// <summary>
        /// Update an array of entities to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int UpdateByPK(IList<T> entities)
        {
            Transaction transaction = null;
            return UpdateByIdPK(entities, false, true, out transaction);
        }

        /// <summary>
        /// Update an array of entities to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int UpdateByPK(IList<T> entities, out Transaction transaction)
        {
            return UpdateByIdPK(entities, false, false, out transaction);
        }

        /// <summary>
        /// Update an array of entities to table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="byId"></param>
        /// <param name="commitTransaction"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        private  int UpdateByIdPK( IList<T> entities, bool byId, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (entities == null || entities.Count <= 0)
            {
                return 0;
            }

            string sql=null;
            List<IDataParameter> paras = null;
            List<string> propertyNames;
            int affectedRowCount = 0;

            ICommandReflector commandReflector = this.CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = this.CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);

            try
            {
                tableCommandExecutor.BeginTransaction();
                foreach (T entity in entities)
                {
                    propertyNames = entity.ChangedPropertyNames;
                    if (propertyNames == null || propertyNames.Count <= 0)
                    {
                        continue;
                    }
                    paras = ParameterReflection.UpdateByIdPK(_databaseType, entity, byId, propertyNames);
                    if (paras == null || paras.Count <= 0)
                    {
                        continue;
                    }

                    sql = byId ? commandReflector.UpdateById(entity, propertyNames) : commandReflector.UpdateByPK(entity, propertyNames);
                    if (tableCommandExecutor.ExecuteNonQuery(sql, false, paras) > 0)
                    {
                        affectedRowCount++;
                        entity.ClearChangedPropertyNames();
                    }
                }
                if (commitTransaction)
                {
                    tableCommandExecutor.Commit();
                }
                return affectedRowCount;
            }
            catch (Exception ex)
            {
                tableCommandExecutor.Rollback();
                throw new CommandException(sql,MsgResource.ExecuteNonQueryFailed, ex);
            }
        }

        #endregion

        #region Delete

        /// <summary>
        /// Delete the entity from table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int DeleteById(T entity)
        {
            ICommandReflector commandReflector = this.CreateCommandReflector();
            string sql = commandReflector.DeleteById(entity);

            int affectedRowCount = DeleteBySql(sql);
            return affectedRowCount;
        }

        /// <summary>
        /// Delete the entity from table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int DeleteById(T entity, out Transaction transaction)
        {
            ICommandReflector commandReflector = this.CreateCommandReflector();
            string sql = commandReflector.DeleteById(entity);

            int affectedRowCount = DeleteBySql(sql, out transaction);
            return affectedRowCount;
        }

        /// <summary>
        /// Delete an array of entities from table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int DeleteById(IList<T> entities)
        {
            Transaction transaction = null;
            return DeleteByIdPK(entities, true, true, out transaction);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int DeleteById(IList<T> entities, out Transaction transaction)
        {
            return DeleteByIdPK(entities, true, false, out transaction);
        }


        /// <summary>
        /// Delete the entity from table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int DeleteByPK(T entity)
        {
            ICommandReflector commandReflector = this.CreateCommandReflector();
            string sql = commandReflector.DeleteByPK(entity);
            int affectedRowCount = DeleteBySql(sql);
            return affectedRowCount;
        }

        /// <summary>
        /// Delete the entity from table,and return the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int DeleteByPK(T entity, out Transaction transaction)
        {
            ICommandReflector commandReflector = this.CreateCommandReflector();
            string sql = commandReflector.DeleteByPK(entity);
            int affectedRowCount = DeleteBySql(sql, out transaction);
            return affectedRowCount;
        }

        /// <summary>
        /// Delete an array of entities from table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int DeleteByPK(IList<T> entities)
        {
            Transaction transaction=null;
            return DeleteByIdPK(entities, false, true, out transaction);
        }

        /// <summary>
        /// Delete an array of entities from table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int DeleteByPK(IList<T> entities, out Transaction transaction)
        {
            return DeleteByIdPK(entities, false, false, out transaction);
        }

        /// <summary>
        /// Delete an array of entities from table,and return the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="byId"></param>
        /// <param name="commitTransaction"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        private int DeleteByIdPK(IList<T> entities, bool byId,bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (entities == null || entities.Count <= 0)
            {
                return 0;
            }

            string sql;
            int affectedRowCount = 0;

            ICommandReflector commandReflector = this.CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = this.CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);
            try
            {
                tableCommandExecutor.BeginTransaction();
                foreach (T entity in entities)
                {
                    sql = byId ? commandReflector.DeleteById(entity) : commandReflector.DeleteByPK(entity);
                    if (tableCommandExecutor.ExecuteNonQuery(sql, false) > 0)
                    {
                        affectedRowCount++;
                    }
                }
                if (commitTransaction)
                {
                    tableCommandExecutor.Commit();
                }
                return affectedRowCount;
            }
            catch (Exception ex)
            {
                tableCommandExecutor.Rollback();
                throw new GatewayException(MsgResource.ExecuteNonQueryFailed, ex);
            }
        }

        /// <summary>
        /// Delete the entity from table,and return the number of rows affected.
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        public int Delete(string condition)
        {
            if (string.IsNullOrEmpty(condition))
            {
                return 0;
            }
             Transaction transaction=null;
             return Delete(condition, true, out transaction);
        }

        /// <summary>
        /// Delete the entity from table,and return the number of rows affected.
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int Delete(string condition, out Transaction transaction)
        {
            transaction = null;
            if (string.IsNullOrEmpty(condition))
            {
                return 0;
            }
            return Delete(condition, false,out transaction);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="commitTransaction"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        private int Delete(string condition, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (string.IsNullOrEmpty(condition))
            {
                return 0;
            }

            ICommandReflector commandReflector = this.CreateCommandReflector();
            string deleteCommandText = commandReflector.Delete(typeof(T), condition);
            if (commitTransaction)
            {
                return DeleteBySql(deleteCommandText);
            }
            else
            {
                return DeleteBySql(deleteCommandText, out transaction);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        private int DeleteBySql(string sql)
        {
            if (string.IsNullOrEmpty(sql))
            {
                return 0;
            }
            ITableCommandExecutor tableCommandExecutor = this.CreateCommandExecutor();
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(sql, false);
            return affectedRowCount;
        }

        private int DeleteBySql(string sql, out Transaction transaction)
        {
            transaction = null;
            if (string.IsNullOrEmpty(sql))
            {
                return 0;
            }
            ITableCommandExecutor tableCommandExecutor = this.CreateCommandExecutor();
            tableCommandExecutor.BeginTransaction();
            transaction = new Transaction(tableCommandExecutor);
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(sql, false);
            return affectedRowCount;
        }

        #endregion //Delete



    }


}